import sys
import traceback 
import Queue
import threading
import logging

"""
Provides class for handling multithread job scheduling.

import scheduler

my_scheduler = scheduler.Scheduler("test_scheduler")
my_scheduler.add_threads(2)
def do_nothing(): print "i'm doing nothing"
#enqueue jobs
my_scheduler.enqueue(do_nothing)
my_scheduler.enqueue(do_nothing)
my_scheduler.enqueue(do_nothing)
#kill
my_scheduler.kill()
"""

class StopJob(object):
	def __init__(self): object.__init__(self)
	def execute(self): pass
	def quit(self): return True


class SchedulerThread(threading.Thread):
	def __init__(self,dispatch_action):
		threading.Thread.__init__(self)
		self.dispatch = dispatch_action
	def run(self):
		logging.debug(threading.currentThread().getName()+" started")
		self.dispatch()
		logging.debug(threading.currentThread().getName()+" ended")

class ThreadPool(object):
	""" represents a pool of threads """
	def __init__(self):
		""" constructor """
		object.__init__(self)
		self.threads = dict()
	def add(self,thread_obj):
		""" add a new thread to this pool
		    @param thread_obj: a Thread instance
		"""
		self.threads[ thread_obj.__hash__() ] = thread_obj 

	def remove(self,thread_obj):
		""" remove a thread object from this pool
		    @param thread_obj: a Thread instance
		"""
		try:
			del self.threads[thread_obj.__hash__()]
		except KeyError: pass  	
	def join_thread(self,thread_obj,timeout = None):
		""" join a thread belonging to this pool
		    @param thread_obj: the Thread instance to join
		    @param timeout: optional max time to wait in seconds	 
		"""
		self.threads[thread_obj.__hash__()].join(timeout)
	def join(self,timeout = None):
		""" join all threads belonging to this pool
		    @param  timeout: optional max time to wait in seconds per thread	
		"""
		for hash, thread_obj in self.threads.items():
			thread_obj.join(timeout)
	def threads(self):
		""" get thread list
		    @return: a list of Thread instances 
		"""
		return self.threads.values()			

	def size(self):
		""" get number of threads in this pool 
		    @return: number of threads
		"""
		return len(self.threads)
	def __len__(self):
		""" get number of threads in this pool 
		    @return: number of threads
		"""
		return len(self.threads)

class Scheduler(object):
	""" a job scheduler """
	
	QUEUE_MAX_CAPACITY = 100

	def __init__(self,name = None):
		""" constructor """
		object.__init__(self)
		self.__thread_list =  ThreadPool()
		self.__queue = Queue.Queue(Scheduler.QUEUE_MAX_CAPACITY)
		self.total = 0
		if name is None: name = ""
		self._name = name

	def size(self):
		""" get queue length
		    @return: size of current queue
		"""
		return len(self.__queue)
	def __len__(self):
		""" get queue length
		    @return: size of current queue
		"""
		return len(self.__queue)
	def empty(self):
		""" test if queue is empty
		    @return: True if queue if empty, False otherwise 
		"""
		if len(self.__queue) ==0: return True
		else: return False	
	
	def add_threads(self,	number):
		""" add some threads
		    @param number: number of threads to add
		"""
		for i in range(	number ):
			thread = SchedulerThread(self.dispatch)
			self.total += 1 
			thread.setName(self._name+"'s "+str(self.total)+"th Thread")
			self.__thread_list.add(thread)
			thread.start()
	def remove_thread(self):
		""" remove a thread """
		self.__queue.put_nowait(StopJob())
	def stop(self):
		""" stop the scheduler """
		for i in range(len(self.__thread_list)):
			self.remove_thread()
	def kill(self):
		""" clear all jobs and stop"""
		try:
			while 1:
				self.__queue.get(block = False)
		except: pass		
		self.stop()
	def enqueue(self,job_obj):
		""" enqueue a job
		    @param job_obj: instance of job   to enqueue
		"""
		self.__queue.put(job_obj)

	def dispatch(self):
		""" method called by dispatcher threads """
		while True:
			try:
				job = self.__queue.get(block = True)
				job()
				#job =  self.__queue.get(block = True)
				#if job.quit(): break
				#job.execute()
			except TypeError:
				break # receive something not callable!
			except:
				traceback.print_exc()
		

	

if __name__ == "__main__":
	#---------------------
	import random,sys,time
	class WaitJob(object):
		def __init__(self,l,m):
			object.__init__(self)
			self.lower = l
			self.higher = m
		def __call__(self):
			rdm = int(random.random() * (self.higher+1-self.lower)) + self.lower
			try:
				print >>sys.stderr,"begin:: wait for",rdm
				time.sleep(rdm*0.01)
				print >>sys.stderr,"end:: wait for",rdm
			except:
				raise
		def quit(self): return False
	#---------------------
	m= Scheduler()
	m.add_threads(15)
	for i in range(100):
		m.enqueue(WaitJob(100,300))
	m.stop()
